﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfBinding
{
    /// <summary>
    /// 按照步骤 1a 或 1b 操作，然后执行步骤 2 以在 XAML 文件中使用此自定义控件。
    ///
    /// 步骤 1a) 在当前项目中存在的 XAML 文件中使用该自定义控件。
    /// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根 
    /// 元素中:
    ///
    ///     xmlns:MyNamespace="clr-namespace:WpfBinding"
    ///
    ///
    /// 步骤 1b) 在其他项目中存在的 XAML 文件中使用该自定义控件。
    /// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根 
    /// 元素中:
    ///
    ///     xmlns:MyNamespace="clr-namespace:WpfBinding;assembly=WpfBinding"
    ///
    /// 您还需要添加一个从 XAML 文件所在的项目到此项目的项目引用，
    /// 并重新生成以避免编译错误:
    ///
    ///     在解决方案资源管理器中右击目标项目，然后依次单击
    ///     “添加引用”->“项目”->[浏览查找并选择此项目]
    ///
    ///
    /// 步骤 2)
    /// 继续操作并在 XAML 文件中使用控件。
    ///
    ///     <MyNamespace:InputNumberBox/>
    ///
    /// </summary>
    public class InputNumberBox : Control
    {
        static InputNumberBox()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(InputNumberBox), new FrameworkPropertyMetadata(typeof(InputNumberBox)));
        }


        #region ValueProperty
        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(decimal), typeof(InputNumberBox),
            new PropertyMetadata(defaultValue,
                new PropertyChangedCallback(ValuePropertyChangedCallback),
                new CoerceValueCallback(ValuePropertyCoerceValueCallback)),
                new ValidateValueCallback(Validate));

        public decimal Value
        {
            get { return (decimal)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }

        static object ValuePropertyCoerceValueCallback(DependencyObject obj, object o)
        {
            decimal value = (decimal)o;
            InputNumberBox control = obj as InputNumberBox;
            return Math.Max(control.MinValue, Math.Min(control.MaxValue, value));
        }

        static void ValuePropertyChangedCallback(DependencyObject Do, DependencyPropertyChangedEventArgs args)
        {
            InputNumberBox control = Do as InputNumberBox;
            RoutedPropertyChangedEventArgs<decimal> routedArgs = new RoutedPropertyChangedEventArgs<decimal>((decimal)args.OldValue, (decimal)args.NewValue, ValueChangedEvent);
            control.RaiseEvent(routedArgs);
        }

        static bool Validate(object target)
        {
            if (target == null)
                return false;
            try
            {
                decimal.Parse(target.ToString());
            }
            catch { return false; }
            return true;
        }
        #endregion

        #region ValueChangedEvent
        public static readonly RoutedEvent ValueChangedEvent = EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble,
             typeof(decimal), typeof(InputNumberBox));

        public event RoutedPropertyChangedEventHandler<decimal> ValueChanged
        {
            add { AddHandler(ValueChangedEvent, value); }
            remove { RemoveHandler(ValueChangedEvent, value); }
        }

        public virtual void OnValueChanged(DependencyObject Do, RoutedPropertyChangedEventArgs<decimal> args)
        {
            this.RaiseEvent(args);
        }
        #endregion

        #region MouseOverBrushProperty
        public static readonly DependencyProperty MouseOverBrushProperty = DependencyProperty.Register("MouseOverBrush",
            typeof(Brush), typeof(InputNumberBox)
            , new PropertyMetadata(Brushes.Green));

        public Brush MouseOverBrush
        {
            get { return (Brush)GetValue(MouseOverBrushProperty); }
            set { SetValue(MouseOverBrushProperty, value); }
        }
        #endregion

        #region MinValueProperty
        public static readonly DependencyProperty MinValueProperty = DependencyProperty.Register("MinValue", typeof(decimal), typeof(InputNumberBox));
        public decimal MinValue
        {
            get { return (decimal)GetValue(MinValueProperty); }
            set { SetValue(MinValueProperty, value); }
        }
        #endregion

        #region MaxValueProperty
        public static readonly DependencyProperty MaxValueProperty = DependencyProperty.Register("MaxValue", typeof(decimal), typeof(InputNumberBox));
        public decimal MaxValue
        {
            get { return (decimal)GetValue(MaxValueProperty); }
            set { SetValue(MaxValueProperty, value); }
        }
        #endregion

        #region const Value
        const decimal defaultValue = decimal.Zero;
        #endregion
    }
}
